2 - Koordinierung [ID:19208]
50 von 139 angezeigt

Als nächstes wollen wir uns anschauen, auf welche Probleme man gegebenenfalls stößt,

wenn man Threads verwendet und welche Mittel man zur Hand hat, um diese Probleme auch wieder

zu lösen.

Dazu schauen wir uns zunächst einmal folgendes Beispiel an.

Hier wollen wir alle Werte eines zweidimensionalen Arrays aufsummieren.

Um diesen Vorgang zu beschleunigen, starten wir für jede Zeile des Arrays einen eigenen

Thread, der die jeweilige Zeile lokal aufsummiert und das Ergebnis der Zeile auf eine globale

Variable addiert.

Dies ist in der SumRow-Funktion implementiert.

Die Frage, die sich stellt, ist, funktioniert das so?

Und die Antwort ist, wenig überraschend, nein, es funktioniert nicht.

Das Problem ist hier in der hervorgebenden Zeile.

Jeder der Threads versucht, sobald er das Ergebnis seiner eigenen Zeile hat, dieses auf

eine einzelne globale Variable aufzuaddieren.

Was nun hier als ein einzelnes Statement aufgezeichnet ist, sind in Wahrheit tatsächlich mehrere

Instruktionen, die ausgeführt werden.

Hier wird der plus-is-gleich-Operator eingesetzt, der tatsächlich zwei verschiedene Schritte

sind.

Zum einen wird der aktuelle Wert von Sum geladen.

Anschließend werden Sum und die Variable localSum zusammengerechnet und in einem dritten

Schritt wird das Ergebnis wieder zurück in die Variable Sum geschrieben.

Nachdem wir nun aber alles parallel machen wollen, kann es sein, dass zwei verschiedene

Threads genau diese eine Code-Zeile ausführen und sich dabei in einem der drei Schritte

befinden.

Wenn wir nun annehmen, dass zwei Threads gleichzeitig die Zeile ausführen wollen, können wir uns

vorstellen, dass beide nun im ersten Schritt den aktuellen Wert Sum laden.

In dem zweiten und dritten Schritt wird das Ergebnis aufaddiert und beide versuchen ihr

jeweiliges Ergebnis wieder zurückzuschreiben.

Was nun aber passiert ist, dass die Threads unabhängig voneinander den Wert Sum gelesen

haben und beide auf demselben Summenwert ihr eigenes Ergebnis aufaddieren und dieses zurückschreiben.

Dabei bleibt nur noch das zuletzt geschriebene Ergebnis erhalten und zwar ohne die Änderung,

die der parallel laufende Thread machen wollte.

Es gilt also mindestens ein Ergebnis verloren.

Dies ist etwas, was wir nicht akzeptieren können.

Deshalb brauchen wir nun eine Möglichkeit, um solches Verhalten zu verhindern.

Und das Werkzeug, das wir in SP an die Hand bekommen, sind sogenannte Semaphore.

Semaphore können zur Koordinierung von Threads verwendet werden.

POSIX bietet bereits eine Schnittstelle für Semaphoren an.

Diese ist aber sehr komplex, da sie auch verwendet werden kann, um verschiedene Prozesse, die

voneinander isoliert laufen, zu koordinieren.

Anstelle der POSIX-Variante verwenden wir eine eigene Implementierung, die sich im Wesentlichen

auf die beiden Operationen P und V beschränkt.

Ein Semaphore ist dabei mehr oder weniger eine Zählervariable und mit der P-Operation

kann die Zählervariable dekrementiert werden, während eine V-Operation sie wieder incrementiert.

Der Trick an der Sache ist jedoch, dass der Aufrufer einer P-Operation blockiert, falls

der Wert einer Zählervariable kleiner als Null werden würde.

Gleichermaßen werden durch den Aufruf der V-Funktion gegebenenfalls blockierte Threads

wieder deblockiert.

Details dazu sind in den Vorlesungsfolien zu finden.

Semaphoren sind dabei sehr vielfältig einsetzbar.

Teil einer Videoserie :
Teil eines Kapitels:
Threads

Zugänglich über

Offener Zugang

Dauer

00:09:49 Min

Aufnahmedatum

2020-07-06

Hochgeladen am

2020-07-06 12:26:32

Sprache

de-DE

Einbetten
Wordpress FAU Plugin
iFrame
Teilen